﻿using System;
using System.Collections;
using System.Threading;
using Komodex.NETMF.MicroTweet;
using MFToolkit.Net.XBee;
using Microsoft.SPOT;
using Microsoft.SPOT.Net.NetworkInformation;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using DCDev.Attendant.Actions;

namespace DCDev.Attendant
{
    public class NodeData
    {
        public string NodeIdentifier { get; protected set; }
        public ZNetRxIoSampleResponse LastResponse { get; set; }

        public NodeData(string nodeIdentifier)
        {
            NodeIdentifier = nodeIdentifier;
            LastResponse = null;
        }
    }

    public class Program
    {
        private static XBee _xbee;
        private static Hashtable _nodes = new Hashtable();
        private static byte _portMask = 0x08;
        private static IAttendantAction _attendantAction;

        public static void Main()
        {
            NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
            NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);

            try
            {                
                NTP.UpdateTimeFromNTPServer("time-a.nist.gov");            
                Debug.Print(DateTime.Now.ToString());
            }
            catch (Exception ex)
            {
                Debug.Print("Error setting time");
                Debug.Print(ex.Message);
            }

            _xbee = new XBee(SerialPorts.COM1, 9600, ApiType.Enabled);
            _xbee.FrameReceived += new FrameReceivedEventHandler(xbee_FrameReceived);
            _xbee.Open();

            // Discover existing XBee devices
            _xbee.Execute(new AtCommand("ND"));

            // Initialize Action
            _attendantAction = new TwitterAction();

            while (true)
            {
                Thread.Sleep(1000);
            }
        }
        
        static void xbee_FrameReceived(object sender, FrameReceivedEventArgs e)
        {
            switch (e.Response.ApiID)
            {
                case XBeeApiType.AtCommandResponse:
                    ProcessAtCommandResponse(e.Response as AtCommandResponse);
                    break;

                case XBeeApiType.NodeIdentificationIndicator:
                    ProcessZNetNodeIdentificationResponse(e.Response as ZNetNodeIdentificationResponse);
                    break;

                case XBeeApiType.ZNetIODataSampleRxIndicator:
                    ProcessZNetRxIoSampleResponse(e.Response as ZNetRxIoSampleResponse);
                    break;

                default:
                    Debug.Print("[Frame Received] " + e.Response.ApiID.ToString());
                    break;
            }
        }

        private static void ProcessAtCommandResponse(AtCommandResponse response)
        {
            if (response != null)
            {
                switch (response.Command)
                {
                    case "ND":
                        ZNetNodeDiscover nodeDiscover = ZNetNodeDiscover.Parse(response);

                        if (!_nodes.Contains(nodeDiscover.SerialNumber.Value))
                        {
                            _nodes.Add(nodeDiscover.SerialNumber.Value, new NodeData(nodeDiscover.NodeIdentifier));
                            Debug.Print("Discovered new node: " + nodeDiscover.NodeIdentifier);
                        }
                        else
                        {
                            // Already know about this node
                            Debug.Print("Discovered existing node: " + nodeDiscover.NodeIdentifier);
                        }
                        break;
                }
            }
        }

        private static void ProcessZNetNodeIdentificationResponse(ZNetNodeIdentificationResponse response)
        {
            if (response != null)
            {
                if (!_nodes.Contains(response.SerialNumber.Value))
                {
                    _nodes.Add(response.SerialNumber.Value, new NodeData(response.NodeIdentifier));
                    Debug.Print("New node added: " + response.NodeIdentifier);
                }
                else
                {
                    // Already know about this node
                    Debug.Print("Existing node: " + response.NodeIdentifier);
                }
            }
        }

        private static void ProcessZNetRxIoSampleResponse(ZNetRxIoSampleResponse response)
        {
            if (response != null)
            {               
                if (_nodes.Contains(response.SerialNumber.Value))
                {
                    NodeData data = (NodeData)_nodes[response.SerialNumber.Value];

                    if ((data.LastResponse != null))
                    {
                        Debug.Print("Old Value: " + data.LastResponse.Digital2.ToString() + ", New Value: " + response.Digital2.ToString());

                        bool oldValue = (data.LastResponse.Digital2 & _portMask) == _portMask;
                        bool newValue = (response.Digital2 & _portMask) == _portMask;

                        if (oldValue != newValue)
                        {
                            Debug.Print("Value changed for " + data.NodeIdentifier);

                            if (_attendantAction.Notify(newValue))
                            {
                                data.LastResponse = response;
                            }
                        }                        
                    }
                    else
                    {
                        Debug.Print("Initializing sample value for " + data.NodeIdentifier);

                        data.LastResponse = response;
                    }                    
                }
            }
        }
        
        static void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
        {
            Debug.Print("Network Address Changed");
        }

        static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            Debug.Print("NetworkChange_NetworkAvailabilityChanged");
            Debug.Print(e.IsAvailable ? "Available" : "Not Available");
        }

    }
}
